﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace test
{
	class Program
	{
		const int size = 350 * 10000;		//350W

		static void Main(string[] args)
		{
			Console.Write("Task 1~3:");
			switch(Console.ReadLine())
			{
				case "1": test1(); break;
				case "2": test2(); break;
				case "3": test3(); break;
			}

			Console.WriteLine("@@@@@@@");
		}

#region test 1
		static void test1()
		{
			serial1();
			parallel1();
		}

		static void serial1()
		{
			for (int aa = 0; aa < 3; aa++)
			{
				DateTime dt = DateTime.Now;	 //进入循环即计时

				//分组数组及初始化
				double[] arr = new double[100];
				for (int i = 0; i < 100; ++i) arr[i] = -1;

				for (int i = 0; i < size; ++i)
				{
					int j = i % 100;	//分组序号即取模值
					if (arr[j] < 0)	 	//如果未计算过,就运行一次数学公式
						arr[j] = Math.Sqrt(j + 1) + (j >> j % 5);
					else
						++arr[j];	   //如果计算过,就递增1
				}

				//将所有分组扩大10000倍后按序号加权求和
				long result = 0;
				for (int i = 0; i < 100; ++i) result += (i + 1) * Convert.ToInt64(Math.Floor(arr[i] * 10000));

				//加权求和完成即截止计时
				var s = (DateTime.Now - dt).TotalMilliseconds;
				Console.WriteLine("SERIAL:		{0}	{1}MS", result, s);
			}
		}

		static void parallel1()
		{
			for (int aa = 0; aa < 1; aa++)
			{
				DateTime dt = DateTime.Now;

				object[] locks = new object[100];
				for (int i = 0; i < 100; ++i) locks[i] = new object();

				double[] arr = new double[100];
				for (int i = 0; i < 100; i++) arr[i] = -1;

				Task[] tasks = new Task[size];

				for (int i = 0; i < size; ++i)
				{
					tasks[i] = Task.Factory.StartNew(j =>
					{
						int k = Convert.ToInt32(j) % 100;
						var val = Math.Sqrt(k + 1) + (k >> k % 5);

						lock (locks[k])
							if (arr[k] < 0)
								arr[k] = val;
							else
								++arr[k];
					}, i);
				}

				//等待所有tasks结束
				try { Task.WaitAll(tasks); }
				catch { }

				//将所有分组扩大10000倍后按序号加权求和
				long result = 0;
				for (var i = 0; i < 100; ++i) result += (i + 1) * Convert.ToInt64(Math.Floor(arr[i] * 10000));

				//加权求和完成即截止计时
				var s = (DateTime.Now - dt).TotalMilliseconds;
				Console.WriteLine("PARALLEL:	{0}	{1}ms", result, s);
			}
		}
#endregion

#region test2
		static void test2()
		{
			byte[] bytes;

			if(File.Exists("file"))
				bytes = File.ReadAllBytes("file");
			else
			{
				Console.WriteLine("DOWNLOADING TEST DATA");
				using (WebClient wc = new WebClient())
					bytes = wc.DownloadData("http://mirrors.163.com/centos/7.5.1804/paas/x86_64/openshift-origin14/openvswitch-2.4.0-2.el7.x86_64.rpm");

				File.WriteAllBytes("file", bytes);
			}
			Console.WriteLine("TEST DATA READY");

			serial2(bytes);
			parallel2(bytes);
			parallel22(bytes);
		}

		static void serial2(byte[] bytes)
		{
			for (int aa = 0; aa < 3; aa++)
			{
				DateTime dt = DateTime.Now;

				Dictionary<uint, int> dict = new Dictionary<uint, int>();

				for (int i = 0; i < bytes.Length - 2; i += 3)
				{
					uint k = Convert.ToUInt32(bytes[i]) << 16 | Convert.ToUInt32(bytes[i + 1]) << 8 | Convert.ToUInt32(bytes[i + 2]);

					if(dict.ContainsKey(k)) ++dict[k]; else dict[k] = 1;
				}

				ulong result = 0;
				foreach(KeyValuePair<uint, int> kvp in dict)
					result += Convert.ToUInt64(kvp.Key) * Convert.ToUInt64(kvp.Value);

				//加权求和完成即截止计时
				var s = (DateTime.Now - dt).TotalMilliseconds;
				Console.WriteLine("SERIAL:		{0}	{1}MS", result, s);
			}
		}

        class ELEM { public object lck = new object(); public int val = 1; }

		static void parallel2(byte[] bytes)
		{
			for (int aa = 0; aa < 5; aa++)
			{
				DateTime dt = DateTime.Now;

				ConcurrentDictionary<uint, ELEM> dict = new ConcurrentDictionary<uint, ELEM>();
				Task[] tasks = new Task[bytes.Length / 3];

				int task_index = -1;
				for (int i = 0; i < bytes.Length - 2; i += 3)
					tasks[++task_index] = Task.Factory.StartNew(j =>
					{
						uint k = Convert.ToUInt32(j);
						k = Convert.ToUInt32(bytes[k]) << 16 | Convert.ToUInt32(bytes[k + 1]) << 8 | Convert.ToUInt32(bytes[k + 2]);

						if (!dict.TryAdd(k, new ELEM()))
							lock (dict[k].lck)
								++dict[k].val;
					}, i);

				//等待所有tasks结束
				try { Task.WaitAll(tasks); }
				catch { }

				ulong result = 0;
				foreach (KeyValuePair<uint, ELEM> kvp in dict)
					result += Convert.ToUInt64(kvp.Key) * Convert.ToUInt64(kvp.Value.val);

				GC.Collect();
				
				//加权求和完成即截止计时
				var s = (DateTime.Now - dt).TotalMilliseconds;
				Console.WriteLine("PARALLEL-:	{0}	{1}MS", result, s);
			}
		}

		static void parallel22(byte[] bytes)
		{
			for (int aa = 0; aa < 5; aa++)
			{
				DateTime dt = DateTime.Now;

				ConcurrentDictionary<uint, ELEM> dict = new ConcurrentDictionary<uint, ELEM>();
				List<(uint a, uint b, uint c)> datas = new List<(uint, uint, uint)>();

				int i = -1;
				while(i < bytes.Length - 2) datas.Add((bytes[++i], bytes[++i], bytes[++i]));

				Parallel.ForEach(datas, tpl =>
				{
					uint k = tpl.a << 16 | tpl.b << 8 | tpl.c;

					if (!dict.TryAdd(k, new ELEM()))
						lock (dict[k].lck)
							++dict[k].val;
				});

				ulong result = 0;
				foreach (KeyValuePair<uint, ELEM> kvp in dict)
					result += Convert.ToUInt64(kvp.Key) * Convert.ToUInt64(kvp.Value.val);

				GC.Collect();
				
				//加权求和完成即截止计时
				var s = (DateTime.Now - dt).TotalMilliseconds;
				Console.WriteLine("PARALLEL+:	{0}	{1}MS", result, s);
			}
		}
#endregion

		static void test3()
		{
			HttpListener server = new HttpListener();
			server.Prefixes.Add("http://*:54321/test/");
			server.Start();

			Action service = null;
			(service = async () =>
			{
				var context = await server.GetContextAsync();
				service();

				var request = context.Request;
				var input = request.InputStream;
				var output = context.Response.OutputStream;

				StreamReader reader = new StreamReader(input);
				StreamWriter writer = new StreamWriter(output);

				if(request.QueryString["r"] == "1")
				{
					string content = reader.ReadToEnd();
					
					if(content == "")
						writer.Write("0");
					else
					{
						int sum = 0;
						foreach(char c in content)
							sum += Convert.ToInt32(c);
						writer.Write($"{content.Length * sum}");
					}
				}
				else
					await writer.WriteAsync(File.ReadAllText("/home/llr/Documents/client.htm"));
				
				writer.Close();
				input.Close();
			})();

			Console.WriteLine("serving...Press ESC to end");
			while(true)
				if(Console.ReadKey(true).Key == ConsoleKey.Escape)
					break;
		}
	}
}